package com.ocom.aiot.controller;

import com.google.gson.Gson;
import com.ocom.aiot.service.AppVerInfoService;
import com.ocom.aiot.service.MjAppVerInfoService;
import com.ocom.aiot.service.MjDeviceInfoService;
import com.ocom.aiot.service.XfDeviceInfoService;
import com.ocom.common.constant.AiotUrl;
import com.ocom.common.entity.aiot.EntityMjAppVerInfo;
import com.ocom.common.entity.aiot.EntityMjDeviceInfo;
import com.ocom.common.entity.aiot.EntityXfAppVerInfo;
import com.ocom.common.entity.aiot.EntityXfDeviceInfo;
import com.ocom.common.enums.MachEnum;
import com.ocom.common.resp.RespMachMessage;
import com.ocom.common.utils.RespUtilMach;
import com.ocom.common.utils.file.BinUtil;
import com.ocom.redis.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: TODO
 * @Author: 朱晖
 * @Date: 2022/8/23
 **/
@Slf4j
@RestController
public class TcpUpdateMachController {


	@Autowired
	private MjDeviceInfoService deviceInfoService;

	@Autowired
	private MjAppVerInfoService appVerInfoService;


	@Autowired
	private XfDeviceInfoService xfDeviceInfoService;

	@Autowired
	private AppVerInfoService XfAppVerInfoService;

	@Resource
	private RedisUtil redisUtil;


	// 获取二进制升级文件数据
	@PostMapping(AiotUrl.URLP_AIOT_getBinData)
	public RespMachMessage getBinData(Long companyId, String machNo, Integer lastBlockNum) throws IOException {
		//判断参数
		if (companyId <=0 || machNo==null|| lastBlockNum<0){
			return RespUtilMach.error(MachEnum.PARAMS_ERROR);
		}

		EntityMjDeviceInfo deviceInfo = deviceInfoService.GetDeviceList(machNo);

		if (lastBlockNum.equals(0)){
			deviceInfoService.updateAppVerLast(companyId,machNo, deviceInfo.getAppVerTab());
		}
		//从门禁设备app版本信息表mj_app_ver_info查找apk_type=2、app_ver=mj_device_info . app_ver_tab的记录
		EntityMjAppVerInfo entityMjAppVerInfo = appVerInfoService.selectSinglechipAppVerInfo(deviceInfo.getAppVerTab());
		Map<String, Object> map = new HashMap<>();
		if (entityMjAppVerInfo !=null){
			//进redis
			String binDate= (String)redisUtil.get("MjMachBinData:url:"+entityMjAppVerInfo.getApkFileUrl());
			if(binDate==null){
//                byte[] fileToByte2 = BinUtil.getFileToByte(entityMjAppVerInfo.getApkFileUrl());
				URL url = new URL(entityMjAppVerInfo.getApkFileUrl());
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(5000);
				conn.setReadTimeout(60000);
				InputStream inputStream = conn.getInputStream();
				byte[] fileToByte2=read(inputStream);
				binDate=new Gson().toJson(fileToByte2);
				redisUtil.set("MjMachBinData:url:"+entityMjAppVerInfo.getApkFileUrl(),new Gson().toJson(fileToByte2),3600L);
			}
			byte[] fileToByte=new Gson().fromJson(binDate,byte[].class);

			int binBlockNum =(int) Math.ceil((double)fileToByte.length/512);

			log.info("lastBlockNum:"+lastBlockNum);

			if (fileToByte.length > 0) {
				Integer blockDataSize = fileToByte.length;
				Integer splitSize = 512;
				int index = (lastBlockNum-1) * splitSize;
				index=index>0?index:0;
				List<Object> blockData = new ArrayList();
				int j = 0;
				while (j < splitSize && index < blockDataSize) {
					blockData.add(byteToHex(fileToByte[index++]));
					j++;
				}
				map.put("blockData", blockData);
				Integer blockSize = blockData.size();
				map.put("blockSize", blockSize);
				map.put("binBlockNum", binBlockNum);
				Long jobId = 512L * lastBlockNum + blockSize;
				//修改Jobid数据
				deviceInfoService.updateJobId(companyId, machNo, jobId);
				map.put("jobId", jobId);
				fileToByte.clone();

			}
		}

		return RespUtilMach.success(map);
	}

	// 更改升级文件数据状态（changeBinDataState）
	@PostMapping(AiotUrl.URLP_AIOT_changeBinDataState)
	public RespMachMessage changeBinDataState(Long companyId, String machNo, Integer jobId){
		//判断参数
		if (companyId <=0 || machNo==null|| jobId<0){
			return RespUtilMach.error(MachEnum.PARAMS_ERROR);
		}
		Map<String, Object> map = new HashMap<>();
		EntityMjDeviceInfo deviceInfo = deviceInfoService.GetDeviceList(machNo);
		if (deviceInfo!=null){
			//1、如果jobId != mj_device_info . app_ jobid则返回flag=0
			if (!jobId.equals(deviceInfo.getAppJobId())){
				map.put("flag",0);
				return RespUtilMach.success(map);
			}
			//2、如果mj_device_info . app_ver_last != mj_device_info . app_ver_tab则返回flag=0
			if (!deviceInfo.getAppVerLast().equals(deviceInfo.getAppVerTab())){
				map.put("flag",0);
				return RespUtilMach.success(map);
			}
			//从门禁设备app版本信息表mj_app_ver_info查找apk_type=2、app_ver=mj_device_info . app_ver_tab的记录，
			EntityMjAppVerInfo entityMjAppVerInfo = appVerInfoService.selectSinglechipAppVerInfo(deviceInfo.getAppVerTab());
			//如果没有记录，返回flag=0
			if (entityMjAppVerInfo == null) {
				map.put("flag", 0);
				return RespUtilMach.success(map);
			}
			try {
				//有记录就打开mj_app_ver_info. apk_file_url文件，无法打开则返回flag=0
				byte[] fileToByte = BinUtil.getFileToByte(entityMjAppVerInfo.getApkFileUrl());
				if (fileToByte.length < jobId) {
					map.put("flag", 0);
					return RespUtilMach.success(map);
				}
				map.put("flag", 255);
				fileToByte.clone();
			} catch (Exception exception) {
				map.put("flag", 0);
				return RespUtilMach.success(map);

			}
		}
		return RespUtilMach.success(map);
	}

	/**
	 * Byte字节转Hex
	 * @param b 字节
	 * @return Hex
	 */
	public static String byteToHex(byte b){
		String hexString = Integer.toHexString(b & 0xFF);
		//由于十六进制是由0~9、A~F来表示1~16，所以如果Byte转换成Hex后如果是<16,就会是一个字符（比如A=10），通常是使用两个字符来表示16进制位的,
		//假如一个字符的话，遇到字符串11，这到底是1个字节，还是1和1两个字节，容易混淆，如果是补0，那么1和1补充后就是0101，11就表示纯粹的11
		if (hexString.length() < 2)
		{
			hexString = new StringBuilder(String.valueOf(0)).append(hexString).toString();
		}
		return hexString.toUpperCase();
	}

	private static byte[] read(InputStream inputStream) {
		ByteArrayOutputStream baos = null;
		try {
			baos = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int num = inputStream.read(buffer);
			while (num != -1) {
				baos.write(buffer, 0, num);
				num = inputStream.read(buffer);
			}
			baos.flush();
			return baos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (baos != null) {
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return null;
	}



	// 获取消费二进制升级文件数据
	@PostMapping(AiotUrl.URLP_AIOT_getXfBinData)
	public RespMachMessage getXfBinData(Long companyId, String machNo, Integer lastBlockNum) throws IOException {
		//判断参数
		if (companyId <=0 || machNo==null|| lastBlockNum<0){
			return RespUtilMach.error(MachEnum.PARAMS_ERROR);
		}

		EntityXfDeviceInfo entityXfDeviceInfo = xfDeviceInfoService.GetDeviceList(machNo);

		if (lastBlockNum.equals(0)){
			xfDeviceInfoService.updateAppVer(companyId,machNo,entityXfDeviceInfo.getAppVerTab());
		}

		EntityXfAppVerInfo entityXfAppVerInfo = XfAppVerInfoService.selectAppVerInfo(entityXfDeviceInfo.getAppVerTab());

		Map<String, Object> map = new HashMap<>();
		if (entityXfAppVerInfo !=null){
			//进redis
			String binDate= (String)redisUtil.get("XfMachBinData:url:"+entityXfAppVerInfo.getApkFileUrl());
			if(binDate==null){
				URL url = new URL(entityXfAppVerInfo.getApkFileUrl());
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(5000);
				conn.setReadTimeout(60000);
				InputStream inputStream = conn.getInputStream();
				byte[] fileToByte2=read(inputStream);
				binDate=new Gson().toJson(fileToByte2);
				redisUtil.set("XfMachBinData:url:"+entityXfAppVerInfo.getApkFileUrl(),new Gson().toJson(fileToByte2),3600L);
			}
			byte[] fileToByte=new Gson().fromJson(binDate,byte[].class);

			int binBlockNum =(int) Math.ceil((double)fileToByte.length/512);

			log.info("lastBlockNum :"+lastBlockNum);

			if (fileToByte.length > 0) {
				Integer blockDataSize = fileToByte.length;
				Integer splitSize = 512;
				int index = (lastBlockNum-1) * splitSize;
				index=index>0?index:0;
				List<Object> blockData = new ArrayList();
				int j = 0;
				while (j < splitSize && index < blockDataSize) {
					blockData.add(byteToHex(fileToByte[index++]));
					j++;
				}
				map.put("blockData", blockData);
				Integer blockSize = blockData.size();
				map.put("blockSize", blockSize);
				map.put("binBlockNum", binBlockNum);
				Long jobId = 512L * lastBlockNum + blockSize;
				//修改Jobid数据
				xfDeviceInfoService.updateJobId(companyId, machNo, jobId);
				map.put("jobId", jobId);
				fileToByte.clone();
			}
		}
		return RespUtilMach.success(map);
	}

	// 更改升级文件数据状态（changeBinDataState）
	@PostMapping(AiotUrl.URLP_AIOT_changeXfBinDataState)
	public RespMachMessage changeXfBinDataState(Long companyId, String machNo, Integer jobId){
		//判断参数
		if (companyId <=0 || machNo==null|| jobId<0){
			return RespUtilMach.error(MachEnum.PARAMS_ERROR);
		}
		Map<String, Object> map = new HashMap<>();
		EntityXfDeviceInfo entityXfDeviceInfo = xfDeviceInfoService.GetDeviceList(machNo);
		if (entityXfDeviceInfo!=null){
			//1、如果jobId != mj_device_info . app_ jobid则返回flag=0
			if (!jobId.equals(entityXfDeviceInfo.getAppJobId())){
				map.put("flag",0);
				return RespUtilMach.success(map);
			}
			//2、如果mj_device_info . app_ver_last != mj_device_info . app_ver_tab则返回flag=0
			if (!entityXfDeviceInfo.getAppVer().equals(entityXfDeviceInfo.getAppVerTab())){
				map.put("flag",0);
				return RespUtilMach.success(map);
			}
			EntityXfAppVerInfo entityXfAppVerInfo = XfAppVerInfoService.selectAppVerInfo(entityXfDeviceInfo.getAppVerTab());
			//如果没有记录，返回flag=0
			if (entityXfAppVerInfo == null) {
				map.put("flag", 0);
				return RespUtilMach.success(map);
			}
			try {
				//有记录就打开mj_app_ver_info. apk_file_url文件，无法打开则返回flag=0
				byte[] fileToByte = BinUtil.getFileToByte(entityXfAppVerInfo.getApkFileUrl());
				if (fileToByte.length < jobId) {
					map.put("flag", 0);
					return RespUtilMach.success(map);
				}
				map.put("flag", 255);
				fileToByte.clone();
			} catch (Exception exception) {
				map.put("flag", 0);
				return RespUtilMach.success(map);

			}
		}
		return RespUtilMach.success(map);
	}



}
